/*
 * ========================================================================
 *
 *       Filename:  testboardpanel.cpp
 *
 *    Description:  This implements the TestBoardPanel class.
 *
 *        Version:  1.0
 *        Created:  05/11/2010 08:50:51 PM
 *       Revision:  none
 *       Compiler:  g++
 *
 *         Author:  Alex Avance (aa), aravance@gmail.com
 *        Company:  independent
 *
 * ========================================================================
 *
 * Copyright (C) 2010 Alex Avance
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-
 * 1307, USA.
 *
 * ========================================================================
 */
#include "testboard.h"
#include "misc.h"
#include "constants.h"
#include "wx/msgdlg.h"
#include "wx/sizer.h"
#include "wx/statbox.h"

BEGIN_EVENT_TABLE( TestBoardPanel, wxPanel )
  EVT_TEXT_ENTER( ID_TestAnswer, TestBoardPanel::OnTextEnter )
  EVT_BUTTON( ID_TestShuffle, TestBoardPanel::OnShuffle )
  EVT_BUTTON( ID_TestReset, TestBoardPanel::OnReset )
  EVT_BUTTON( ID_TestSkip, TestBoardPanel::OnSkip )
  EVT_BUTTON( ID_TestEnter, TestBoardPanel::OnEnter )
  EVT_BUTTON( ID_TestDraw, TestBoardPanel::OnDraw )
END_EVENT_TABLE()

/**************************************************************************
Class Name: TestBoardPanel
Method Name: TestBoardPanel
Description: constructor
**************************************************************************/
TestBoardPanel::TestBoardPanel ( wxFrame *parent, StudyDeck &deck )
  : wxPanel( (wxWindow*)parent, wxID_ANY ), StatInfo(), m_Parent( parent ), m_Deck( deck )
{
  // ==================== Main Panel         =============================================
  wxBoxSizer *sizerParent= new wxBoxSizer( wxVERTICAL );
  wxBoxSizer *sizerMain= new wxBoxSizer( wxVERTICAL );
  wxStaticBoxSizer *sizerText= new wxStaticBoxSizer( wxVERTICAL, this );
  wxBoxSizer *sizerButtons= new wxBoxSizer( wxHORIZONTAL );

  // ==================== Text Fields        =============================================
  m_TextQuestion= new wxStaticText( this, wxID_ANY, _(""), wxDefaultPosition, wxDefaultSize,
                                    wxST_NO_AUTORESIZE, _("Question") );
  m_TextResult= new wxStaticText( this, wxID_ANY, _(""),
                                  wxDefaultPosition, wxDefaultSize, 0, _("Result") );

  m_TextAnswer= new CustomTextCtrl( this, ID_TestAnswer, _(""), wxDefaultPosition, wxDefaultSize,
                                    wxTE_PROCESS_ENTER | wxTE_MULTILINE, wxDefaultValidator, _("Answer") );

  sizerText->Add( m_TextQuestion, 1, wxEXPAND );
  sizerText->Add( m_TextResult, 0, wxEXPAND );

  sizerMain->Add( sizerText, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 5 );

  sizerMain->Add( m_TextAnswer, 1, wxEXPAND | wxALL, 5 );

  // ==================== Buttons            =============================================
  sizerButtons->AddStretchSpacer( 1 );

  m_ButtonReset= new wxButton( this, ID_TestReset, _("&Reset") );
  m_ButtonShuffle= new wxButton( this, ID_TestShuffle, _("Sh&uffle") );
  m_ButtonSkip= new wxButton( this, ID_TestSkip, _("S&kip") );
  m_ButtonEnter= new wxButton( this, ID_TestEnter, _("&Enter") );
  m_ButtonDraw= new wxButton( this, ID_TestDraw, _("&Draw") );

  sizerButtons->Add( m_ButtonReset, 0, wxALIGN_CENTER | wxALL, 5 );
  sizerButtons->Add( m_ButtonShuffle, 0, wxALIGN_CENTER | wxALL, 5 );
  sizerButtons->Add( m_ButtonSkip, 0, wxALIGN_CENTER | wxALL, 5 );
  sizerButtons->Add( m_ButtonEnter, 0, wxALIGN_CENTER | wxALL, 5 );
  sizerButtons->Add( m_ButtonDraw, 0, wxALIGN_CENTER | wxALL, 5 );

  sizerMain->Add( sizerButtons, 0, wxEXPAND | wxALL, 5 );

  // ==================== Sizers             =============================================
  sizerParent->Add( sizerMain, 1, wxEXPAND | wxALL, 5 );

  SetSizer( sizerParent );

  m_TextAnswer->SetFocus();

  m_SideChoice= FrontSide;

  m_IgnoreCase= false;
  m_IgnorePunct= false;
  m_KeepRight= false;
  m_Modified= false;
  m_SessionActive= false;

  Reset();
} // ----- end of method TestBoardPanel::TestBoardPanel (constructor) -----

/**************************************************************************
Class Name: TestBoardPanel
Method Name: TestBoardPanel
Description: destructor
**************************************************************************/
TestBoardPanel::~TestBoardPanel ()
{} // ----- end of method TestBoardPanel::TestBoardPanel (destructor) -----

/**************************************************************************
Class: TestBoardPanel
Method: IsModified
Description: This returns true if the deck has been modified
**************************************************************************/
bool TestBoardPanel::IsModified () const
{ return m_Modified; } // ----- end of method TestBoardPanel::IsModified -----

/**************************************************************************
Class: TestBoardPanel
Method: Unmodified
Description: Change the deck to unmodified.
**************************************************************************/
void TestBoardPanel::Unmodified ()
{ m_Modified= false; } // ----- end of method TestBoardPanel::Unmodified -----

/**************************************************************************
Class: TestBoardPanel
Method: Reset
Description: Revert the board back to it's initial state. This does not
             do anything to modification of the deck.
**************************************************************************/
void TestBoardPanel::Reset ()
{
  ResetDecks();

  m_TextQuestion->SetLabel( _("") );
  m_TextResult->SetLabel( _("") );
  m_TextAnswer->SetValue( _("") );

  m_ButtonReset->Disable();
  m_ButtonShuffle->Disable();
  m_ButtonSkip->Disable();
  m_ButtonEnter->Disable();
  m_ButtonDraw->Enable();

  m_ButtonEnter->Hide();
  m_ButtonDraw->Show();

  m_TextAnswer->SetEditable( false );

  m_SessionActive= false;

  Update();
} // ----- end of method TestBoardPanel::Reset -----

/**************************************************************************
Class: TestBoardPanel
Method: Update
Description: Update the panel.
**************************************************************************/
void TestBoardPanel::Update ()
{
  UpdateStatus();

  if ( m_Deck.Size() > 1 )
    m_ButtonDraw->Enable();
  else
    m_ButtonDraw->Disable();

  Layout();
} // ----- end of method TestBoardPanel::Update -----

/**************************************************************************
Class: TestBoardPanel
Method: UpdateStatus
Description: Update the status bar.
**************************************************************************/
void TestBoardPanel::UpdateStatus ()
{
  wxString status;
  status.Printf( _("Session: %d/%d\t\t"
                   "Deck: %d/%d"),
                 Right(),
                 Wrong(),
                 m_Deck.Right() + m_RightPile.Right(),
                 m_Deck.Wrong() + m_RightPile.Wrong() );

  if ( m_SessionActive )
    status.Append( wxString::Format( _("\t\tCard: %d/%d"),
                                     m_Deck.Top().Right(), m_Deck.Top().Wrong() ) );

  m_Parent->SetStatusText( status );
} // ----- end of method TestBoardPanel::UpdateStatus -----

/**************************************************************************
Class: TestBoardPanel
Method: DrawCard
Description: Draw the top card.
**************************************************************************/
void TestBoardPanel::DrawCard ()
{
  if ( ( m_SideChoice == FrontSide ) || ( m_SideChoice == RandomSide && rand() % 2 ) )
  {
    m_Question= m_Deck.Top().Front();
    m_Answer= m_Deck.Top().Back();
  }
  else
  {
    m_Question= m_Deck.Top().Back();
    m_Answer= m_Deck.Top().Front();
  }

  m_TextQuestion->SetLabel( m_Question );
} // ----- end of method TestBoardPanel::DrawCard -----

/**************************************************************************
Class: TestBoardPanel
Method: ResetDecks
Description: This reshuffles all the decks back together.
**************************************************************************/
void TestBoardPanel::ResetDecks ()
{
  m_Deck.Append( m_RightPile );
  m_RightPile.Clear();
} // ----- end of method TestBoardPanel::ResetDecks -----

/**************************************************************************
Class: TestBoardPanel
Method: Guess
Description: Make a given guess.
**************************************************************************/
bool TestBoardPanel::Guess ( wxString guess )
{
  wxString answer= m_Answer;
  bool result;

  if ( m_IgnoreCase )
  {
    guess.MakeLower();
    answer.MakeLower();
  }

  if ( m_IgnorePunct )
  {
    StripPunct( guess );
    StripPunct( answer );
  }

  result= ( guess == answer );

  m_Deck.Guess( result );
  StatInfo::Guess( result );

  m_Modified= true;

  return result;
} // ----- end of method TestBoardPanel::Guess -----

/**************************************************************************
Class: TestBoardPanel
Method: Show
Description: Show the panel.
**************************************************************************/
bool TestBoardPanel::Show ( bool show )
{
  m_ButtonReset->Disable();
  m_ButtonShuffle->Disable();
  m_ButtonSkip->Disable();

  m_ButtonDraw->Show();
  m_ButtonDraw->Enable( m_Deck.Size() + m_RightPile.Size() > 0 );

  m_ButtonEnter->Hide();
  m_ButtonEnter->Disable();

  m_TextAnswer->SetFocus();
  m_TextAnswer->SetEditable( false );

  return wxPanel::Show( show );
} // ----- end of method TestBoardPanel::Show -----

/**************************************************************************
Class: TestBoardPanel
Method: ConfirmSwitch
Description: Confirm switching away from the current board.
**************************************************************************/
bool TestBoardPanel::ConfirmSwitch ( bool force )
{
  int result= wxYES;

  if ( m_SessionActive )
  {
    if ( !force )
      result= wxMessageBox( _("Abandon the current test session?"), _("Confirm Switch"),
                            wxICON_QUESTION | wxYES_NO, this );

    if ( result == wxNO )
      return false;
    else
      Reset();
  }

  return ( result == wxYES );
} // ----- end of method TestBoardPanel::ConfirmSwitch -----

/**************************************************************************
Class: TestBoardPanel
Method: MenuCheckItems
Description: These handle the menu check item information.
**************************************************************************/
void TestBoardPanel::OnIgnoreCase ( wxCommandEvent &event )
{ m_IgnoreCase= !m_IgnoreCase; } // ----- end of method TestBoardPanel::OnIgnoreCase -----

void TestBoardPanel::OnIgnorePunct ( wxCommandEvent &event )
{ m_IgnorePunct= !m_IgnorePunct; } // ----- end of method TestBoardPanel::OnIgnorePunct -----

void TestBoardPanel::OnKeepRight ( wxCommandEvent &event )
{ m_KeepRight= !m_KeepRight; } // ----- end of method TestBoardPanel::OnKeepRight -----

void TestBoardPanel::OnRandomSide ( wxCommandEvent &event )
{ m_SideChoice= RandomSide; } // ----- end of method TestBoardPanel::OnRandomSide -----

void TestBoardPanel::OnFrontSide ( wxCommandEvent &event )
{ m_SideChoice= FrontSide; } // ----- end of method TestBoardPanel::OnFrontSide -----

void TestBoardPanel::OnBackSide ( wxCommandEvent &event )
{ m_SideChoice= BackSide; } // ----- end of method TestBoardPanel::OnBackSide -----

/**************************************************************************
Class: TestBoardPanel
Method: OnTextEnter
Description: Handle enter press inside the text control.
**************************************************************************/
void TestBoardPanel::OnTextEnter ( wxCommandEvent &event )
{
  // draw state means draw button
  if ( m_ButtonDraw->IsShown() )
    OnDraw( event );
  // enter state means enter button
  else
    OnEnter( event );
} // ----- end of method TestBoardPanel::OnTextEnter -----

/**************************************************************************
Class: TestBoardPanel
Method: OnReset
Description: Reshuffle all decks back together. 
**************************************************************************/
void TestBoardPanel::OnReset ( wxCommandEvent &event )
{
  ResetDecks();
  DrawCard();
  UpdateStatus();
} // ----- end of method TestBoardPanel::OnReset -----

/**************************************************************************
Class: TestBoardPanel
Method: OnShuffle
Description: Shuffle the deck.
**************************************************************************/
void TestBoardPanel::OnShuffle ( wxCommandEvent &event )
{
  // shuffle the deck
  m_Deck.Shuffle();
  DrawCard();
  UpdateStatus();
} // ----- end of method TestBoardPanel::OnShuffle -----

/**************************************************************************
Class: TestBoardPanel
Method: OnSkip
Description: This skips a card.
**************************************************************************/
void TestBoardPanel::OnSkip ( wxCommandEvent &event )
{
  // go to the next card and draw it
  m_Deck.Next();
  DrawCard();
  UpdateStatus();
} // ----- end of method TestBoardPanel::OnSkip -----

/**************************************************************************
Class: TestBoardPanel
Method: OnEnter
Description: This handles guessing and such.
**************************************************************************/
void TestBoardPanel::OnEnter ( wxCommandEvent &event )
{
  // make sure there's text in the control
  if ( m_TextAnswer->GetValue() == _("") )
    wxMessageBox( _("Please enter a guess"), _("No Guess"),
                  wxICON_EXCLAMATION | wxOK, this );
  else
  {
    // make the given guess
    bool correct= Guess( m_TextAnswer->GetValue() );

    // show correct vs incorrect
    m_TextResult->SetLabel( correct ? _("Correct") : _("Incorrect") );

    // set up for the next draw
    m_ButtonReset->Disable();
    m_ButtonShuffle->Disable();
    m_ButtonSkip->Disable();

    m_ButtonDraw->Show();
    m_ButtonDraw->Enable();

    m_ButtonEnter->Hide();
    m_ButtonEnter->Disable();

    m_TextAnswer->SetFocus();
    m_TextAnswer->SetEditable( false );
    m_TextAnswer->SetValue( m_Answer );

    Layout();

    UpdateStatus();

    if ( m_KeepRight || !correct )
      m_Deck.Next();
    else
    {
      m_RightPile.Insert( 0, m_Deck.Top() );
      m_Deck.Pop();
    }
  }
} // ----- end of method TestBoardPanel::OnEnter -----

/**************************************************************************
Class: TestBoardPanel
Method: OnDraw
Description: Draw a card.
**************************************************************************/
void TestBoardPanel::OnDraw ( wxCommandEvent &event )
{
  int result= wxYES;

  // clean the text controls
  m_TextQuestion->SetLabel( _("") );
  m_TextResult->SetLabel( _("") );
  m_TextAnswer->SetValue( _("") );

  // if there aren't cards, ask about resetting the deck
  if ( m_Deck.Count() == 0 && m_RightPile.Count() > 0 )
  {
    result= wxMessageBox( _("There are no cards left in the active deck.\nReset the deck?"),
                          _("Reset Deck"), wxICON_QUESTION | wxYES_NO, this );

    if ( result == wxYES )
      ResetDecks();
  }

  // if it reset, draw a card
  if ( result == wxYES )
  {
    m_SessionActive= true;

    DrawCard();
    UpdateStatus();

    if ( m_Deck.Count() > 1 )
    {
      m_ButtonReset->Enable();
      m_ButtonShuffle->Enable();
      m_ButtonSkip->Enable();
    }
    else
    {
      m_ButtonReset->Disable();
      m_ButtonShuffle->Disable();
      m_ButtonSkip->Disable();
    }

    m_ButtonDraw->Hide();
    m_ButtonDraw->Disable();

    m_ButtonEnter->Show();
    m_ButtonEnter->Enable();

    m_TextAnswer->SetEditable( true );
    m_TextAnswer->SetFocus();

    Layout();
  }
} // ----- end of method TestBoardPanel::OnDraw -----

